home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / prog / asm_0_m.arj / E.ASM < prev    next >
Assembly Source File  |  1989-08-19  |  52KB  |  1,978 lines

  1. ;Programmer's editor.  Assemble with TASM.  (C) David Nye, 1989.
  2.  
  3. IDEAL
  4. DOSSEG
  5. MODEL TINY
  6.  
  7. MACRO String Name, Text
  8. LOCAL L1
  9. Name    db L1-Name-1, Text
  10. L1:
  11. ENDM
  12.  
  13. ;Constants
  14. LINELENGTH      EQU 80          ;Length of string storage for line
  15. SCREENLENGTH    EQU 24          ;Number of rows in display window
  16. MAXLINES        EQU 2000        ;Size of array of line pointers
  17. BUFFERLENGTH    EQU 1200h       ;Length of file buffer
  18. BELL            EQU 7           ;Some non-printing chars
  19. BS              EQU 8
  20. HT              EQU 9
  21. LF              EQU 10
  22. CR              EQU 13
  23. CRLF            EQU 0A0Dh
  24. CTRL_Z          EQU 26
  25. ESCAPE          EQU 27
  26. DEL             EQU 127
  27.  
  28. DATASEG
  29. ;Strings
  30. String cantOpenMsg,     "Can't open file."
  31. String rdErrorMsg,      'Error reading file.'
  32. String fileErrorMsg,    'File error.'
  33. String noRoomMsg,       'Out of memory.'
  34. String notMarkingMsg,   'Not marking.'
  35. String setLabelMsg,     'Label (0-9): '
  36. String setTabsMsg,      'Tab width: '
  37. String newFileMsg,      'File name: '
  38. String gotoMsg          'Jump to what line? '
  39. String editingMsg       <'Help F1',186,'Editing: '>
  40. String findMsg          'Find: '
  41. String replaceMsg       'Replace with: '
  42. String notFoundMsg      'No more matching strings found.'
  43. String endHelpMsg       'Press any key to continue.'
  44. String ctrlCMsg         '*Break*'
  45. String cancelledMsg     'Cancelled.'
  46. BAK                     db  '.BAK', 0
  47. tempFile                db  'e.tmp', 0
  48. helpMsg                 db 'left                left arrow, ^s      '
  49.                         db '          begin block            @b     '
  50.                         db 'right               right arrow, ^d     '
  51.                         db '          copy block to buffer   @c *   '
  52.                         db 'word left           ^left arrow, ^a     '
  53.                         db '          delete block to buffer @d *   '
  54.                         db 'word right          ^right arrow, ^f    '
  55.                         db '          insert block from buf  @i *   '
  56.                         db 'start of line       Home                '
  57.                         db '          empty block buffer     @e *   '
  58.                         db 'end of line         End                 '
  59.                         db '          unmark                 @u     '
  60.                         db 'up                  up arrow, ^e        '
  61.                         db 40 dup ( )
  62.                         db 'down                down arrow, ^x      '
  63.                         db '          find                   @f +   '
  64.                         db 'page up             PgUp, ^r            '
  65.                         db '          replace                @r +   '
  66.                         db 'page down           PgDn, ^c            '
  67.                         db '          find/replace all       @= +   '
  68.                         db 'start of file       ^PgUp               '
  69.                         db 40 dup ( )
  70.                         db 'end of file         ^PgDn               '
  71.                         db '          save and continue      @s     '
  72.                         db 40 dup ( )
  73.                         db '          save and exit          @x     '
  74.                         db 'backspace           Backspace           '
  75.                         db '          kill save on exit      @k     '
  76.                         db 'delete              Del                 '
  77.                         db 40 dup ( )
  78.                         db 'delete word left    ^[                  '
  79.                         db '          label (0-9)            @l     '
  80.                         db 'delete word right   ^], ^t              '
  81.                         db '          go to label (0-9)      @g     '
  82.                         db 'delete to EOL       ^\                  '
  83.                         db '          jump to line #         @j     '
  84.                         db 'delete line         ^-, ^y              '
  85.                         db '          set tab width          @t     '
  86.                         db 'undelete line       ^^                  '
  87.                         db '          toggle autoindent      @a     '
  88.                         db 'toggle insert mode  Ins                 '
  89.                         db '          open another file      @o     '
  90.                         db 80 dup ( )
  91.                         db '@ = Alt, ^ = Ctrl, * = to/from file if s'
  92.                         db 'hifted, + = use last string if shifted. '
  93.                         db 'Status line flags:  Insert  Overwrite  C'
  94.                         db 'hanged  AutoInsert                      '
  95.  
  96.  
  97. ;Variables
  98. attribNl        db 7            ;Default video attributes
  99. attribInv       db 70h
  100. tabSize         db 4            ;Tab increment
  101. newFile?        db ?            ;True if new file
  102. inserting?      db -1           ;True if in insert mode
  103. autoIndent?     db -1           ;True if in autoindent mode
  104. marking?        db 0            ;True if marking text
  105. changed?        db 0            ;True if original file has been changed
  106. isBAKed?        db 0            ;True if .BAK file already written
  107. justFound?      db ?            ;True if no other commands since last Find
  108. needCopies?     db -1           ;True unless lines in buffer were just deleted
  109. autoReplace?    db 0            ;-1 if auto-replace with shift, 1 without shift
  110. fName?          db -1           ;True if file name given on command line
  111. row             db ?            ;Current row
  112. column          db ?            ;Current column
  113. fHandle         dw ?            ;File handle
  114. lastLine        dw ?            ;Index of last line
  115. top             dw ?            ;Index of first line on screen
  116. bottom          dw ?            ;Index of last line on screen
  117. bufferPtr       dw ?            ;Multipurpose buffer pointer
  118. mark            dw ?            ;Start of marking for block command
  119. here            dw ?            ;Temporary
  120. hereCol         dw ?            ;Temporary
  121. videoSegment    dw ?            ;Segment of system's video memory
  122. fName           db 20 dup (?)   ;File name in ASCIIZ format
  123. fNameBAK        db 20 dup (?)   ;Current file with .BAK extension added
  124. fNameBlock      db 20 dup (?)   ;File to be used in block read/writes
  125. pad             db 20 dup (?)   ;Scratch buffer
  126. heapStart       dw ?            ;Segment of start of heap
  127. heapPtr         dw ?            ;Segment pointer to next free paragraph in heap
  128. labelTable      dw 10 dup (0)   ;Table of line pointers assigned to labels
  129. findString      dw LINELENGTH dup (?)  ;Search string for Find command
  130. replaceString   dw LINELENGTH dup (?)  ;New string for Replace command
  131. linePtrs        dw MAXLINES dup (?) ;List of line pointers
  132. blockPtrs       dw MAXLINES dup (?) ;Line pointers for block commands
  133. blockPtrsLast   dw OFFSET blockPtrs
  134. delLinePtrs     dw MAXLINES dup (?) ;Line pointers for del/undel lines
  135. delLinePtrsLast dw OFFSET delLinePtrs
  136. buffer          db BUFFERLENGTH dup (?)  ;File and delete buffer (dual purpose)
  137. bufferEnd:
  138.  
  139. ;Jump tables:   ^ = Ctrl, @ = Alt, # = Shift.
  140. ctrlTable       dw na           ;^@
  141.                 dw WordLeft     ;^A
  142.                 dw na           ;^B
  143.                 dw PageDown     ;^C
  144.                 dw Right        ;^D
  145.                 dw Up           ;^E
  146.                 dw WordRight    ;^F
  147.                 dw na           ;^G or BEL
  148.                 dw BackSpace    ;^H or BS
  149.                 dw Tab          ;^I or HT
  150.                 dw na           ;^J or LF
  151.                 dw na           ;^K or VT
  152.                 dw na           ;^L or FF
  153.                 dw CRet         ;^M or CR
  154.                 dw na           ;^N or SO
  155.                 dw na           ;^O or SI
  156.                 dw na           ;^P
  157.                 dw na           ;^Q or DC1
  158.                 dw PageUp       ;^R or DC2
  159.                 dw Left         ;^S or DC3
  160.                 dw DeleteWordR  ;^T or DC4
  161.                 dw na           ;^U
  162.                 dw na           ;^V
  163.                 dw na           ;^W
  164.                 dw Down         ;^X or CAN
  165.                 dw DeleteLine   ;^Y
  166.                 dw na           ;^Z
  167.                 dw DeleteWordL  ;^[ or ESC
  168.                 dw DeleteToEOL  ;^\
  169.                 dw DeleteWordR  ;^]
  170.                 dw UndeleteLine ;^^
  171.                 dw DeleteLine   ;^-
  172.  
  173. auxTable        dw 15 DUP (na)  ;Undefined (except for NULL = 3)
  174.                 dw ReverseTab   ;#Tab
  175.                 dw na           ;@Q
  176.                 dw na           ;@W
  177.                 dw EmptyBuffer  ;@E
  178.                 dw Replace      ;@R
  179.                 dw SetTabs      ;@T
  180.                 dw na           ;@Y
  181.                 dw Unmark       ;@U
  182.                 dw InsertBlock  ;@I
  183.                 dw OtherFile    ;@O
  184.                 dw na           ;@P
  185.                 dw 4 DUP (na)   ;Undefined
  186.                 dw AutoIndent   ;@A
  187.                 dw Save         ;@S
  188.                 dw DeleteBlock  ;@D
  189.                 dw Find         ;@F
  190.                 dw GotoLabel    ;@G
  191.                 dw Help         ;@H
  192.                 dw Jump         ;@J
  193.                 dw Kill         ;@K
  194.                 dw SetLabel     ;@L
  195.                 dw 5 DUP (na)   ;Undefined
  196.                 dw na           ;@Z
  197.                 dw Exit         ;@X
  198.                 dw Copy         ;@C
  199.                 dw na           ;@V
  200.                 dw BeginBlock   ;@B
  201.                 dw na           ;@N
  202.                 dw na           ;@M
  203.                 dw 8 DUP (na)   ;Undefined
  204.                 dw Help         ;F1
  205.                 dw na           ;F2
  206.                 dw na           ;F3
  207.                 dw na           ;F4
  208.                 dw na           ;F5
  209.                 dw na           ;F6
  210.                 dw na           ;F7
  211.                 dw na           ;F8
  212.                 dw na           ;F9
  213.                 dw na           ;F10
  214.                 dw 2 DUP (na)   ;Undefined
  215.                 dw HomeLine     ;Home
  216.                 dw Up           ;Up arrow
  217.                 dw PageUp       ;PgUp
  218.                 dw na           ;Undefined
  219.                 dw Left         ;Left arrow
  220.                 dw na           ;Undefined
  221.                 dw Right        ;Right arrow
  222.                 dw na           ;Undefined
  223.                 dw EndLine      ;End
  224.                 dw Down         ;Down arrow
  225.                 dw PageDown     ;PgDn
  226.                 dw ToggleIns    ;Ins
  227.                 dw Delete       ;Del
  228.                 dw 30 DUP (na)  ;[#Fn, ^Fn, @Fn]
  229.                 dw na           ;^PrtSc
  230.                 dw WordLeft     ;^Left arrow
  231.                 dw WordRight    ;^Right arrow
  232.                 dw na           ;^End
  233.                 dw BottomFile   ;^PgDn
  234.                 dw na           ;^Home
  235.                 dw 10 DUP (na)  ;[Alt numbers]
  236.                 dw na           ;@-
  237.                 dw AutoReplace  ;@=
  238.                 dw TopFile      ;^PgUp
  239.  
  240. ;******************************************************************************
  241.  
  242. CODESEG
  243.   mov ax, cs
  244.   mov es, ax
  245.   mov ah, 0Fh                   ;Get display segment
  246.   int 10h
  247.   mov bx, 0B000h                ;B000h for video mode 7 (= MDA or Herc)
  248.   cmp al, 7
  249.   je @@L2
  250.   mov bx, 0B800h                ;B800h for the rest
  251. @@L2:
  252.   mov [cs:videoSegment], bx
  253.   mov si, 80h                   ;Make pointer to command tail
  254.   mov cl, [si]                  ;Get filename length
  255.   sub ch, ch
  256.   push cx                       ;Save a copy
  257.   mov al, ' '                   ;Skip leading blanks
  258. @@L1:
  259.   inc si
  260.   cmp al, [si]
  261.   loope @@L1
  262.   inc cx
  263.   mov di, OFFSET fName          ;Move command tail to FName
  264.   rep movsb
  265.   sub al, al                    ;Make ASCIIZ string
  266.   stosb
  267.   mov ax, 2523h                 ;Redirect Ctrl C handler
  268.   mov dx, SEG Cancel
  269.   mov ds, dx
  270.   mov dx, OFFSET Cancel
  271.   int 21h
  272. InitFile:
  273.   mov ax, cs                    ;Compute starting segment of heap
  274.   mov ds, ax
  275.   add ax, 1000h
  276.   mov [heapStart], ax
  277.   mov dx, OFFSET fName          ;Open file and set up list of line pointers
  278.   pop ax                        ;If no file name specified on command line,
  279.   or ax, ax
  280.   jne @@L0
  281.   call GetFileName              ;Prompt for it
  282. @@L0:
  283.   call OpenFile
  284.  
  285. NextKey:
  286.   call Redraw                   ;Redraw screen, status line
  287. NextNoRedraw:
  288.   call DrawCursor               ;Place cursor
  289.   sub ah, ah                    ;Get keypress to AL
  290.   int 16h
  291.   or al, al                     ;Check for control codes
  292.   je IsAux
  293.   cmp al, ' '
  294.   jl IsCtrl
  295.   call Insert                   ;Insert or overwrite if none
  296.   jmp NextKey
  297.  
  298. IsAux:
  299.   xchg al, ah                   ;Get aux code
  300.   cmp al, 132
  301.   ja NextKey
  302.   mov si, OFFSET auxTable       ;Jump indirect to appropriate routine
  303. DoTableJump:
  304.   shl ax, 1
  305.   add si, ax
  306.   call [WORD si]
  307.   jmp NextKey
  308.  
  309. IsCtrl:
  310.   mov si, OFFSET ctrlTable      ;Jump to routine through table
  311.   sub ah, ah
  312.   jmp DoTableJump
  313.  
  314. OtherFile:
  315. ;Close current file and open another
  316.   test [changed?], -1           ;If previous file was changed,
  317.   jz @@L0
  318.   call Save                     ;Save file and close it,
  319.   jmp SHORT @@L1
  320. @@L0:
  321.   test [newFile?], -1           ;If unchanged but an old file, just close it
  322.   jnz @@L2
  323. @@L1:
  324.   mov bx, [fHandle]
  325.   mov ah, 3Eh
  326.   int 21h
  327. @@L2:
  328.   mov ax, [blockPtrsLast]       ;If block buffer is empty,
  329.   sub ax, OFFSET blockPtrs
  330.   jne @@L2a
  331.   mov dx, OFFSET fName          ; prompt for new file name
  332.   call GetFileName
  333.   jmp SHORT OpenFile            ; read it in
  334. @@L2a:
  335.   shr ax, 1                     ;Else move lines with pointers in block buffer
  336.   mov cx, ax                    ; to start of heap (load new file above them)
  337.   mov dl, 5
  338.   mul dl
  339.   add ax, [heapStart]           ;Calculate upper limit of target zone
  340.   mov [heapPtr], ax             ; which will also be new value of heap pointer
  341.   mov bx, OFFSET blockPtrs      ;For each pointer in block buffer,
  342. @@L3:
  343.   cmp [bx], ax                  ;If its line is already within target zone,
  344.   jae @@L4
  345.   mov es, [bx]                  ;Set high bit of first char in line to mark it
  346.   or [byte es:0], 80h           ; (we won't need to move these lines)
  347. @@L4:
  348.   inc bx                        ;Next pointer
  349.   inc bx
  350.   loop @@L3
  351.   push ds
  352.   mov bx, OFFSET blockPtrs      ;For each pointer in block buffer:
  353.   mov es, [heapStart]
  354. @@L4a:
  355.   test [byte es:0], 80h         ;If high bit set in target line,
  356.   jne @@L7                      ; line already in use, try next target line
  357.   mov ds, [cs:bx]               ;If high bit set in source line,
  358.   test [byte 0], 80h
  359.   je @@L5                       ; don't need to move it (already there)
  360.   inc bx                        ; Next source line, same target line
  361.   inc bx
  362.   jmp SHORT @@L8
  363. @@L5:
  364.   mov cx, 40                    ;Else move one line
  365.   sub si, si
  366.   sub di, di
  367.   rep movsw
  368.   mov [cs:bx], es               ;Update pointer
  369. @@L6:
  370.   inc bx                        ;Next block pointer
  371.   inc bx
  372. @@L7:
  373.   mov ax, es                    ;Next target line
  374.   add ax, 5
  375.   mov es, ax
  376. @@L8:
  377.   cmp bx, [cs:blockPtrsLast]    ;Loop until all lines moved
  378.   jb @@L4a
  379.   pop ds
  380.   mov bx, OFFSET blockPtrs      ;Reset high bits of all first chars set high
  381. @@L9:
  382.   mov es, [bx]
  383.   and [byte es:0], 7Fh
  384.   inc bx
  385.   inc bx
  386.   cmp bx, [blockPtrsLast]
  387.   jb @@L9
  388.   mov dx, OFFSET fName          ;Prompt for new file name
  389.   call GetFileName
  390.   jmp SHORT OpenFile1           ;Read in new file above block buffer lines
  391.  
  392. OpenFile:
  393. ;Open file, load if found.  Call with dx -> ASCIIZ file name.
  394.   mov ax, OFFSET blockPtrs      ;Reset block buffer pointer
  395.   mov [blockPtrsLast], ax
  396.   mov ax, [heapStart]           ;Begin loading at start of heap
  397.   mov [heapPtr], ax
  398. OpenFile1:
  399.   mov [newFile?], 0
  400.   mov [changed?], 0
  401.   mov ax, 3D02h                 ;Try to open file
  402.   int 21h
  403.   jnc OldFile
  404.   mov [newFile?], -1            ;If no such file, create a new one
  405.   call NewFile
  406.   jmp XOpenFile
  407. OldFile:
  408.   mov [fHandle], ax             ;Else save file handle
  409.   mov bx, OFFSET linePtrs       ;Read file in
  410.   mov dx, [fHandle]
  411.   call ReadFile
  412.   dec bx
  413.   dec bx
  414.   mov [lastLine], bx            ;Save index of last line
  415. XOpenFile:
  416.   mov bx, OFFSET linePtrs       ;Reset row, screen pointers
  417.   mov [top], bx
  418.   mov es, [bx]
  419.   sub di, di
  420. Ret0:
  421.   ret
  422.  
  423. GetFileName:
  424. ;Prompt for file name.  Abort if null name.  Call with buffer address in DX.
  425.   push si
  426.   push dx
  427.   mov si, OFFSET newFileMsg     ;Print prompt
  428.   call Prompt
  429.   pop dx
  430.   call GetString                ;Get file name
  431.   mov si, dx                    ;Convert to ASCIIZ
  432.   add si, ax
  433.   mov [BYTE si], 0
  434. @@Lx:
  435.   pop si
  436. Ret5:
  437.   ret
  438.  
  439. GetString:
  440. ;Get string to [DX], return count (minus CR/LF) in AX.  Abort if null string.
  441.   push bx
  442.   push cx
  443.   push si
  444.   push di
  445.   push es
  446.   push dx
  447.   mov dx, OFFSET pad            ;Get string
  448.   mov ah, 3Fh
  449.   sub bx, bx
  450.   mov cx, 20
  451.   int 21h
  452.   dec ax                        ;Strip CR/LF
  453.   dec ax
  454.   jz @@La                       ;Abort if null string
  455.   mov cx, ax                    ;Copy temporary copy of string to [DX]
  456.   pop dx
  457.   push ax
  458.   mov ax, ds
  459.   mov es, ax
  460.   mov si, OFFSET pad
  461.   mov di, dx
  462.   rep movsb
  463.   pop ax
  464.   pop es
  465.   pop di
  466.   pop si
  467.   pop cx
  468.   pop bx
  469.   ret
  470. @@La:
  471.   mov si, OFFSET cancelledMsg   ;Abort if null string
  472.   jmp Abort
  473.  
  474. ReadFile:
  475. ;Load file with handle in DX into memory, assigning segment pointers from BX
  476.   push es                                         
  477.   mov ax, [heapPtr]                             
  478.   mov es, ax
  479.   sub cx, cx
  480.   sub di, di
  481. FillBuffer:                                       
  482.   push bx                       ; Fill buffer
  483.   push cx
  484.   push dx
  485.   mov bx, dx
  486.   mov ah, 3Fh
  487.   mov cx, BUFFERLENGTH
  488.   mov dx, OFFSET buffer
  489.   int 21h
  490.   jnc @@L1                      ; Check for read error
  491.   jmp ReadError
  492. @@L1:
  493.   pop dx
  494.   pop cx
  495.   pop bx
  496.   mov si, OFFSET buffer         ; Set pointers
  497.   add ax, si
  498.   mov [bufferPtr], ax
  499.   cmp ax, OFFSET buffer         ;Exit if empty buffer
  500.   je EndOfFile
  501.   cmp [byte si], LF             ;Skip LF if first char in buffer
  502.   jne SHORT NextLine
  503.   inc si
  504. NextLine:
  505.   mov al, [si]                  ;Get next char
  506.   cmp al, CR                    ;If char is CR, end of line
  507.   jne @@L2
  508.   inc si                        ; move past CR
  509.   cmp [byte si], LF             ; and LF if present
  510.   jne @@L1
  511.   inc si
  512. @@L1:
  513.   call EndOfLine                ; pad out line with spaces and save it
  514.   jmp SHORT @@L3
  515. @@L2:
  516.   cmp al, HT                    ;Else if a tab, expand it
  517.   jne @@L2a
  518.   push cx
  519.   mov al, ' '
  520.   mov cl, [tabSize]
  521.   sub ch, ch
  522.   rep stosb
  523.   pop cx
  524.   sub cl, [tabSize]
  525.   sbb ch, 0
  526.   inc si
  527.   jmp SHORT @@L3
  528. @@L2a:
  529.   movsb                         ;Else add char to line
  530.   dec cx
  531. @@L3:
  532.   cmp si, [bufferPtr]           ;Loop until end of buffer
  533.   jb NextLine
  534.   cmp si, OFFSET bufferEnd      ;If buffer less than full, indicates end of file
  535.   jae FillBuffer
  536. EndOfFile:
  537.   cmp bx, OFFSET linePtrs       ;If an empty file,
  538.   jne @@L0
  539.   call NewFile                  ; set up as a new file (single blank line)
  540.   jmp SHORT @@L1
  541. @@L0:
  542.   or di, di                     ;Finish up present line if anything is on it
  543.   je @@L1
  544.   call EndOfLine
  545. @@L1:
  546.   mov [heapPtr], es             ;Update pointer to start of free heap space
  547.   pop es
  548.   ret
  549.  
  550. EndOfLine:
  551.   add cx, LINELENGTH            ;Pad to end with spaces
  552.   jle @@L1                      ;Truncate lines longer than 80 chars
  553.   mov al, ' '
  554.   rep stosb
  555. @@L1:
  556.   mov [bx], es                  ;Store segment of this line
  557.   mov ax, es                    ;Next line
  558.   add ax, 5
  559.   cmp ax, 0A000h                ;Out of room?
  560.   jae SHORT TooBig
  561.   mov es, ax
  562.   inc bx
  563.   inc bx
  564.   sub di, di
  565.   sub cx, cx
  566. Ret3:
  567.   ret
  568. TooBig:
  569.   mov si, OFFSET noRoomMsg
  570.   jmp Abort
  571.  
  572. Redraw:
  573. ;Redraw screen and status line
  574.   mov [here], bx
  575.   mov [hereCol], di
  576.   push bx
  577.   push di
  578.   push ds
  579.   push es
  580.   push di
  581.   mov es, [videoSegment]        ;Get segment for display
  582.   mov si, OFFSET editingMsg     ;Refresh status line:  "Editing ..."
  583.   call Prompt
  584.   mov di, 34                    ;Tab to column 17
  585.   mov si, OFFSET fName          ; <file name>
  586.   mov ah, [attribInv]
  587. @@S1:
  588.   lodsb
  589.   or al, al
  590.   je @@S2
  591.   stosw
  592.   jmp @@S1
  593. @@S2:
  594.   add di, 6                     ;3 spaces
  595.   mov al, 'L'                   ;"L" <line #>
  596.   stosw
  597.   inc di
  598.   inc di
  599.   mov ax, bx
  600.   sub ax, OFFSET linePtrs
  601.   shr ax, 1
  602.   inc ax
  603.   call PrintInt
  604.   add di, 4                     ;2 spaces
  605.   mov al, 'C'                   ;"C" <column number>
  606.   mov ah, [attribInv]
  607.   stosw
  608.   inc di
  609.   inc di
  610.   pop ax                        ;Get copy of DI as cursor row
  611.   inc ax
  612.   call PrintInt
  613.   mov di, 152                   ;Tab to column 76
  614.   mov al, 'I'                   ;Insert/Overwrite status
  615.   mov ah, [attribInv]
  616.   test [inserting?], -1
  617.   jne @@S3
  618.   mov al, 'O'
  619. @@S3:
  620.   stosw
  621.   mov al, 'C'                   ;Changed status
  622.   test [changed?], -1
  623.   jne @@S5
  624.   mov al, ' '
  625. @@S5:
  626.   stosw
  627.   mov al, 'A'
  628.   test [autoIndent?], -1
  629.   jne @@S6
  630.   mov al, ' '
  631. @@S6:
  632.   stosw
  633.   mov di, 160                   ;Move to next display line
  634.   mov ax, [top]                 ;Compute bottom of screen
  635.   mov bx, ax
  636.   add ax, (SCREENLENGTH - 1) * 2
  637.   cmp ax, [lastLine]            ;If at end of file,
  638.   jle @@L0
  639.   mov ax, [lastLine]            ; stop at lastLine
  640. @@L0:
  641.   mov [bottom], ax
  642. @@L1:                           ;For each row
  643.   mov cx, 80                    ;Count of chars per row
  644.   mov ds, [cs:bx]               ;Get pointer to screen line
  645.   sub si, si                    ;Initialize column counter
  646.   mov ah, [cs:attribNl]         ;Attribute = inverse video if Marked
  647.   test [cs:marking?], -1
  648.   je @@L2
  649.   cmp bx, [cs:mark]
  650.   je @@L1b
  651.   jb @@L1a
  652.   cmp bx, [cs:here]
  653.   jbe @@L1b
  654.   jmp SHORT @@L2
  655. @@L1a:
  656.   cmp bx, [cs:here]
  657.   jb @@L2
  658. @@L1b:
  659.   mov ah, [cs:attribInv]
  660. @@L2:                           ;For each char, write char and attribute
  661.   lodsb
  662.   stosw
  663.   loop @@L2                     ;Next char
  664.   inc bx                        ;Next row
  665.   inc bx
  666.   cmp bx, [cs:bottom]           ;Stop if screen full
  667.   jle @@L1
  668.   mov cx, 160*(SCREENLENGTH+1)  ;Fill out screen with blanks
  669.   sub cx, di
  670.   shr cx, 1
  671.   mov ah, [cs:attribNl]
  672.   mov al, ' '
  673.   rep stosw
  674. @@L3:
  675.   pop es
  676.   pop ds
  677.   pop di
  678.   pop bx
  679.   ret
  680.  
  681. DrawCursor:
  682. ;Set cursor shape and place it on screen
  683.   push bx
  684.   mov cl, 13                    ;Set cursor shape depending on Inserting?
  685.   mov ch, 12                    ;Line for insert mode,
  686.   test [Inserting?], -1
  687.   jne @@L1
  688.   sub ch, ch                    ;Block for overwrite
  689. @@L1:
  690.   mov ah, 1
  691.   int 10h
  692.   sub bx, [top]                 ;Show cursor at current row, column
  693.   shr bx, 1
  694.   inc bx
  695.   mov dh, bl
  696.   mov ax, di
  697.   mov dl, al
  698.   mov ah, 2
  699.   mov bh, 0
  700.   int 10h
  701.   pop bx
  702.   ret
  703.  
  704. Print0:
  705.   call ClearStatus              ;Blank status line
  706.   sub di, di                    ;Starting at beginning of line ...
  707.  
  708. Print:
  709. ;Print string pointed to by SI on status line in inverse video, starting at DI
  710.   push es
  711.   mov es, [videoSegment]
  712.   lodsb                         ;Get count of string to be printed
  713.   mov cl, al
  714.   sub ch, ch
  715.   mov ah, [attribInv]            ;Attribute = inverse video
  716. @@L1:
  717.   lodsb
  718.   stosw
  719.   loop @@L1
  720.   pop es
  721.   ret
  722.  
  723. ClearStatus:
  724. ;Inverse-blank status line
  725.   push di
  726.   push es
  727.   mov es, [videoSegment]
  728.   mov ah, [attribInv]
  729.   mov al, ' '
  730.   mov cx, 80
  731.   sub di, di
  732.   rep stosw
  733.   pop es
  734.   pop di
  735.   ret
  736.  
  737. Cancel:
  738. ;Ctrl C routine
  739.   mov si, OFFSET ctrlCMsg       ;Abort with message ...
  740.  
  741. Abort:
  742. ;Print counted string pointed to by SI on status line and abort
  743.   call Print0                   ;Print error message ...
  744.  
  745. na:
  746. ;Unassigned key or other error.  Beep and abort.
  747.   call Beep                     ;Beep
  748.   mov ax, -2                    ;Reset stack pointer to top
  749.   mov sp, ax
  750.   mov bx, [here]                ;Retrieve cursor position in case it was trashed
  751.   mov di, [hereCol]
  752.   call DrawCursor
  753.   jmp NextNoRedraw              ;Restart main editing loop
  754.  
  755. Beep:
  756.   mov ah, 2                     ;Output a BELL
  757.   mov dl, BELL
  758.   int 21h
  759.   ret
  760.  
  761. Prompt:
  762.   push di
  763.   call Print0                   ;Print string at start of line
  764.   mov dx, di                    ;Set cursor to end of printed string ...
  765.   shr dl, 1
  766.   sub dh, dh
  767.   pop di
  768.  
  769. GotoXY:
  770. ;Position cursor at row, column given by DL, DH
  771.   push bx
  772.   mov ah, 2
  773.   sub bx, bx
  774.   int 10h
  775.   pop bx
  776.   ret
  777.  
  778. PrintInt:
  779. ;Print to ES:DI in inverse video the unsigned decimal integer in AX
  780.   sub dx, dx                    ;Start stack with a null
  781.   push dx
  782.   or ax, ax                     ;If integer = 0,
  783.   jne @@L0
  784.   push dx                       ; push another 0 and skip divisions
  785.   jmp SHORT @@L2
  786. @@L0:
  787.   mov cx, 10                    ;Get remainders of successive divisions by 10
  788. @@L1:
  789.   div cx
  790.   add dl, '0'                   ;Convert to ASCII
  791.   mov dh, [attribInv]            ;Attribute is reverse video
  792.   push dx
  793.   sub dx, dx
  794.   or ax, ax
  795.   jne @@L1
  796. @@L2:
  797.   pop ax                        ;Pop and print remainders in reversed order
  798. @@L3:
  799.   stosw
  800.   pop ax
  801.   or ax, ax
  802.   jne @@L3
  803.   ret
  804.  
  805. NewLine:
  806. ;Jump here if cursor row is changed by a command
  807.   mov [justFound?], 0
  808. NewLine0:
  809.   cmp bx, OFFSET linePtrs       ;Check bounds, adjust if necessary
  810.   jge @@L1
  811.   mov bx, OFFSET linePtrs
  812. @@L1:
  813.   cmp bx, [lastLine]
  814.   jle @@L2
  815.   mov bx, [lastLine]
  816. @@L2:
  817.   mov ax, [top]    
  818.   cmp bx, ax
  819.   jge @@L3
  820.   mov [top], bx
  821. @@L3:
  822.   add ax, (SCREENLENGTH-1)*2
  823.   cmp bx, ax
  824.   jle @@L4
  825.   mov ax, bx
  826.   sub ax, (SCREENLENGTH-1)*2
  827.   mov [top], ax
  828. @@L4:
  829.   mov es, [bx]                  ;Adjust ES to point to new line
  830.   ret
  831.  
  832. Left:
  833.   or di, di                     ;If at start of line,
  834.   jne @@L1
  835.   call Up                       ; move to end of line above
  836.   jmp EndLine
  837. @@L1:
  838.   dec di                        ; else just decrement cursor
  839. CursorMoved:
  840.   mov [justFound?], 0
  841.   ret
  842.  
  843. Right:
  844.   cmp di, LINELENGTH - 1        ;If at end of line,
  845.   jne @@L1
  846.   sub di, di                    ; move to start of line below
  847.   jmp Down
  848. @@L1:
  849.   inc di                        ; else just increment cursor
  850.   jmp SHORT CursorMoved
  851.  
  852. LScanE:
  853. ;Scan left past first non-space or start of line
  854.   mov al, ' '      
  855.   mov cx, di
  856.   inc cx
  857.   std
  858.   repe scasb
  859.   cld
  860.   ret
  861.  
  862. LScanNE:
  863. ;Scan left past first space or start of line
  864.   mov al, ' '
  865.   mov cx, di
  866.   inc cx
  867.   std
  868.   repne scasb
  869.   cld
  870.   ret
  871.  
  872. RScanE:
  873. ;Scan right past first non-space or end of line
  874.   mov al, ' '
  875.   mov cx, LINELENGTH
  876.   sub cx, di
  877.   repe scasb
  878.   ret
  879.  
  880. RScanNE:
  881. ;Scan right past first space or end of line
  882.   mov al, ' '
  883.   mov cx, LINELENGTH
  884.   sub cx, di
  885.   repne scasb
  886.   ret
  887.  
  888. WordLeft:
  889. ;Move left one word
  890.   or di, di                     ;Do nothing if at start of line
  891.   je @@Lx
  892.   mov [justFound?], 0
  893.   dec di                        ;Else starting at char to left,
  894.   call LScanE                   ; skip spaces until non-space
  895.   inc di
  896.   je @@Lx                       ; or start of line,
  897.   call LScanNE                  ; then scan to next space or start of line
  898.   jne @@L1
  899.   inc di
  900. @@L1:
  901.   inc di
  902. @@Lx:
  903.   ret
  904.  
  905. WordRight:
  906. ;Move right one word
  907.   cmp di, LINELENGTH - 1        ;Do nothing if at end of line
  908.   je @@Lx
  909.   mov [justFound?], 0
  910.   call RScanNE                  ;Skip non-spaces until space
  911.   jne @@L1                      ; or end of line,
  912.   dec di
  913.   call RScanE                   ; then scan to next non-space or end of line
  914. @@L1:
  915.   dec di
  916. @@Lx:
  917.   ret
  918.  
  919. HomeLine:
  920. ;Move cursor to column zero
  921.   sub di, di
  922.   mov [justFound?], 0
  923.   ret
  924.  
  925. EndLine:
  926. ;Move cursor to end of line
  927.   push cx
  928.   mov [justFound?], 0
  929.   mov di, LINELENGTH - 1        ;Start at end of line
  930.   call LScanE                   ;Skip all spaces until non-space
  931.   je @@L1                       ; or beginning of line
  932.   inc di
  933.   cmp di, LINELENGTH - 1
  934.   je @@L2
  935. @@L1:
  936.   inc di
  937. @@L2:
  938.   pop cx
  939. Ret2:
  940.   ret
  941.  
  942. Up:
  943. ;Move cursor up one line
  944.   cmp bx, OFFSET linePtrs       ;If at top of file already, do nothing
  945.   je Ret2
  946.   dec bx
  947.   dec bx
  948.   jmp NewLine
  949.  
  950. Down:
  951. ;Move cursor down one line
  952.   cmp bx, [lastLine]            ;If at last line already, do nothing
  953.   je Ret2
  954.   inc bx
  955.   inc bx
  956.   jmp NewLine
  957.  
  958. PageUp:
  959. ;Move cursor up one page
  960.   sub bx, (SCREENLENGTH-1)*2
  961.   jmp NewLine
  962.  
  963. PageDown:
  964. ;Move cursor down one page
  965.   add bx, (SCREENLENGTH-1)*2
  966.   jmp NewLine
  967.  
  968. TopFile:
  969. ;Move cursor to top of file
  970.   mov bx, OFFSET linePtrs
  971.   mov [top], bx
  972.   call HomeLine
  973.   jmp NewLine
  974.  
  975. BottomFile:
  976. ;Move cursor to bottom of file
  977.   mov bx, [lastLine]
  978.   mov es, [bx]
  979.   mov ax, bx
  980.   sub ax, (SCREENLENGTH-1)*2
  981.   cmp ax, OFFSET linePtrs
  982.   ja @@L1
  983.   mov ax, OFFSET linePtrs
  984. @@L1:
  985.   mov [top], ax
  986.   call EndLine
  987.   jmp NewLine
  988.  
  989. Tab:
  990. ;Tab right
  991.   mov [justFound?], 0
  992.   mov ax, di                    ;Advance di to next tab stop
  993.   mov cl, [tabSize]
  994.   div cl
  995.   sub cl, ah
  996.   sub ch, ch
  997.   add di, cx
  998.   cmp di, LINELENGTH            ;If past end of line,
  999.   jl @@L1
  1000.   mov di, LINELENGTH - 1        ;Set cursor at end of line
  1001. @@L1:
  1002.   ret
  1003.  
  1004. ReverseTab:
  1005. ;Tab left
  1006.   mov [justFound?], 0
  1007.   mov ax, di                    ;Decrement di to nearest tab stop
  1008.   dec al
  1009.   div [tabSize]
  1010.   mov al, ah
  1011.   sub ah, ah
  1012.   inc al
  1013.   sub di, ax
  1014.   jnc @@L1                      ;Set to start of line if past start
  1015.   sub di, di
  1016. @@L1:
  1017.   ret
  1018.  
  1019. CRet:
  1020. ;Split line at cursor
  1021.   push ds
  1022.   push es
  1023.   push di
  1024.   push es
  1025.   call InsertLine               ;Start a new line below current one, ->ES:DI
  1026.   pop ds                        ;DS:SI := current cursor position
  1027.   pop si
  1028.   push di
  1029.   mov cx, LINELENGTH            ;CX := # chars left on line
  1030.   sub cx, si
  1031.   je @@L2
  1032. @@L1:
  1033.   movsb                         ;Split line,
  1034.   mov [byte si - 1], ' '        ; blank original to end from cursor
  1035.   loop @@L1
  1036. @@L2:
  1037.   pop di
  1038.   pop es
  1039.   pop ds
  1040.   mov [changed?], -1            ;Mark file as changed
  1041.   jmp NewLine
  1042.  
  1043. InsertLine:
  1044. ;Insert a new blank line below current one
  1045.   mov cx, 1                     ;Make room for new entry in linePtr
  1046.   call OpenRow
  1047.   inc bx
  1048.   inc bx
  1049.   mov ax, [heapPtr]
  1050.   jmp SHORT BlankLine
  1051.  
  1052. NewFile:
  1053. ;Set up initial blank line of a new file
  1054.   mov ax, [heapStart]           ;Set ES and [bx] to available heap
  1055.   mov bx, OFFSET linePtrs
  1056.   mov [lastLine], bx
  1057. BlankLine:
  1058.   mov [bx], ax
  1059.   mov es, ax
  1060.   add ax, 5
  1061.   mov [heapPtr], ax             ;Update heap pointer (segment value only)
  1062.   sub di, di                    ;Blank new line
  1063.   mov cx, LINELENGTH
  1064.   mov al, ' '
  1065.   rep stosb
  1066.   sub di, di                    ;Home cursor on new line
  1067.   test [autoIndent?], -1        ; or if in autoindent mode,
  1068.   je @@Lx
  1069.   cmp bx, OFFSET linePtrs       ; and this is not first line in file,
  1070.   je @@Lx
  1071.   mov es, [bx - 2]              ; line up with first char of line above
  1072.   call RScanE
  1073.   mov es, [bx]
  1074.   dec di
  1075.   cmp di, LINELENGTH - 1        ; unless above line is blank
  1076.   jb @@Lx
  1077.   sub di, di
  1078. @@Lx:
  1079.   ret
  1080.  
  1081. OpenRow:
  1082. ;Open CX lines at BX in linePtrs
  1083.   push cx
  1084.   push di
  1085.   push es
  1086.   mov ax, ds                    ;DS, ES -> data segment (for linePtr)
  1087.   mov es, ax
  1088.   mov si, [lastLine]            ;SI points to last line's segment pointer
  1089.   mov di, si                    ;DI points CX lines beyond that
  1090.   add di, cx
  1091.   add di, cx
  1092.   mov [lastLine], di            ;Point lastLine to new last line
  1093.   mov cx, si                    ;Count = # lines from here to end
  1094.   sub cx, bx
  1095.   shr cx, 1
  1096.   inc cx
  1097.   std                                                                
  1098.   rep movsw                     ;Move array elements up
  1099.   cld
  1100.   pop es
  1101.   pop di
  1102.   pop cx
  1103.   ret
  1104.  
  1105. Backspace:
  1106. ;Delete char to left of cursor
  1107.   mov ax, di                    ;Unless at first character of file,
  1108.   add ax, bx
  1109.   sub ax, OFFSET linePtrs
  1110.   jz Ret1                       ; do Left then Delete
  1111.   mov [justFound?], 0
  1112.   push di
  1113.   call Left
  1114.   pop ax                        ;Don't do Join if already at end of line ...
  1115.   or ax, ax
  1116.   jne Delete0
  1117.  
  1118. Delete:
  1119. ;Delete char at cursor
  1120.   mov dx, di                    ;Save cursor column
  1121.   cmp [byte ptr es:LINELENGTH-1], ' ' ;If deleting a space at end of line,
  1122.   jne Delete0
  1123.   call EndLine
  1124.   xchg di, dx
  1125.   cmp di, dx
  1126.   jge Join                      ; join to line below
  1127. Delete0:
  1128.   push di                       ; else slide text left
  1129.   push cx
  1130.   push ds
  1131.   mov cx, LINELENGTH - 1
  1132.   sub cx, di
  1133.   mov si, di
  1134.   inc si
  1135.   mov ax, es
  1136.   mov ds, ax
  1137.   rep movsb
  1138.   mov [BYTE di], ' '            ;Blank last character on line
  1139.   pop ds
  1140.   pop cx
  1141.   pop di
  1142.   mov [changed?], -1
  1143. Ret1:
  1144.   ret
  1145.  
  1146. UndeleteLine:
  1147.   mov bp, [delLinePtrsLast]     ;Abort if no lines are in buffer
  1148.   cmp bp, OFFSET delLinePtrs
  1149.   ja @@L0
  1150.   jmp Beep
  1151. @@L0:
  1152.   dec bp                        ;Else move pointer to top line of delete buffer
  1153.   dec bp
  1154.   mov [delLinePtrsLast], bp
  1155.   or di, di                     ;If cursor is at start of line,
  1156.   jne @@L1
  1157.   mov cx, 1
  1158.   call OpenRow                  ;Start new row below current one
  1159.   mov [bx+2], es                ;Swap rows to insert undeleted above current
  1160.   mov ax, [bp]                  ;Retrieve and store pointer to undeleted line
  1161.   mov [bx], ax
  1162.   jmp NewLine
  1163. @@L1:
  1164.   mov cx, LINELENGTH            ;Cursor not at start of line
  1165.   sub cx, di                    ;Copy popped line over current one
  1166.   push di
  1167.   push ds
  1168.   mov ds, [bp]
  1169.   sub si, si
  1170.   rep movsb
  1171.   pop ds
  1172.   pop di
  1173.   ret
  1174.  
  1175. Join:
  1176. ;Join lower line to current line at cursor
  1177.   cmp bx, [lastLine]            ;Abort if this is the last line of the file
  1178.   je @@Lx
  1179.   push di                       ;Save registers
  1180.   push ds
  1181.   push di
  1182.   push es
  1183.   mov es, [bx + 2]              ;Get next line's segment
  1184.   push es                       ;Save a copy
  1185.   mov dx, di                    ;Get length of lower line:
  1186.   call EndLine                  ;Find first non-space char from end
  1187.   add dx, di                    ;If concatenated line is too long, abort.
  1188.   cmp dx, LINELENGTH
  1189.   jbe @@L0
  1190.   call Beep
  1191.   pop ax
  1192.   pop es
  1193.   pop di
  1194.   pop ds
  1195.   pop ax
  1196.   jmp Ret1
  1197. @@L0:
  1198.   mov cx, di                    ;Count = lower line length
  1199.   sub si, si                    ;Source = start of lower line
  1200.   pop ds
  1201.   pop es                        ;Destination = present cursor location
  1202.   pop di
  1203.   rep movsb                     ;Concatenate lines
  1204.   pop ds
  1205.   inc bx                        ;Delete lower line
  1206.   inc bx
  1207.   call DeleteLineNS
  1208.   cmp bx, [lastLine]
  1209.   je @@L1
  1210.   dec bx
  1211.   dec bx
  1212. @@L1:
  1213.   pop di                        ;Restore pointers and return
  1214. @@Lx:
  1215.   jmp NewLine
  1216.  
  1217. Insert:
  1218. ;Insert or overwrite at cursor
  1219.   mov [justFound?], 0
  1220.   test [inserting?], -1         ;If inserting, open up space for new character
  1221.   jz Insert1
  1222.   cmp [BYTE es:LINELENGTH - 1], ' ' ;If line is full, split it
  1223.   je Insert0
  1224.   push ax
  1225.   push bx
  1226.   push di
  1227.   call CRet
  1228.   pop di
  1229.   pop bx
  1230.   call NewLine
  1231.   pop ax
  1232.   jmp SHORT Insert1
  1233. Insert0:
  1234.   push ax
  1235.   push cx
  1236.   push ds
  1237.   mov ax, es
  1238.   mov ds, ax
  1239.   mov si, LINELENGTH - 1
  1240.   mov cx, si
  1241.   sub cx, di
  1242.   mov di, si
  1243.   dec si
  1244.   std
  1245.   rep movsb
  1246.   cld
  1247.   pop ds
  1248.   pop cx
  1249.   pop ax
  1250. Insert1:
  1251.   stosb                         ;Add character
  1252.   mov [changed?], -1
  1253.   cmp di, LINELENGTH            ;Don't advance DI if at end of line
  1254.   jb @@L1
  1255.   dec di
  1256. @@L1:
  1257.   ret
  1258.  
  1259. DeleteToEOL:
  1260. ;Delete from cursor to end of line
  1261.   mov [justFound?], 0
  1262.   push bx                       ;Save regs to return to current cursor position
  1263.   push di
  1264.   push es
  1265.   push [word autoIndent?]       ;Turn autoIndent off
  1266.   mov [autoIndent?], 0
  1267.   call Cret                     ;Do Enter then delete lower line
  1268.   call DeleteLine
  1269.   pop [word autoIndent?]
  1270.   pop es
  1271.   pop di
  1272.   pop bx
  1273.   jmp NewLine
  1274.  
  1275. DeleteLine:
  1276. ;Delete cursor line and append to buffer
  1277.   mov bp, [delLinePtrsLast]     ;Save segment of current line in delete buffer
  1278.   mov [bp], es
  1279.   inc bp
  1280.   inc bp
  1281.   mov [delLinePtrsLast], bp
  1282. DeleteLineNS:                   ;Enter here if we don't want to save line
  1283.   mov di, bx                    ;Delete line:  destination = this line
  1284.   mov si, di                    ;Source = next line
  1285.   inc si
  1286.   inc si
  1287.   mov cx, [lastLine]            ;Count = number of lines from here to end
  1288.   mov ax, cx
  1289.   dec ax
  1290.   dec ax
  1291.   mov [lastLine], ax
  1292.   sub cx, bx
  1293.   shr cx, 1
  1294.   mov ax, ds                    ;Move line segment values above cursor down
  1295.   mov es, ax     
  1296.   rep movsw
  1297.   mov [changed?], -1
  1298.   sub di, di                    ;Home cursor on new line
  1299.   jmp NewLine
  1300.  
  1301. DeleteWordL:
  1302. ;Delete left to space or column zero
  1303.   mov si, di                    ;Save cursor column
  1304.   call WordLeft                 ;Tab left one word
  1305.   push di
  1306. CloseGap:
  1307.   mov cx, LINELENGTH            ;Close gap between di and si cursor positions
  1308.   sub cx, si
  1309.   push ds
  1310.   mov ax, es
  1311.   mov ds, ax
  1312.   rep movsb
  1313.   mov cx, LINELENGTH            ;Pad end of line with spaces
  1314.   sub cx, di
  1315.   mov al, ' '
  1316.   rep stosb
  1317.   mov [changed?], -1
  1318.   pop ds
  1319.   pop di
  1320.   ret
  1321.  
  1322. DeleteWordR:
  1323. ;Delete right to space or end of line
  1324.   mov si, di                    ;Save cursor
  1325.   push di
  1326.   call WordRight                ;Tab right one word
  1327.   xchg si, di                   ;Close up space between si and di
  1328.   jmp CloseGap
  1329.  
  1330. ToggleIns:
  1331.   not [inserting?]
  1332.   ret
  1333.  
  1334. Jump:
  1335. ;Jump to line number n
  1336.   mov si, OFFSET gotoMsg
  1337.   call Prompt
  1338.   call GetInt
  1339.   dec ax
  1340.   shl ax, 1
  1341.   mov bx, ax
  1342.   add bx, OFFSET linePtrs
  1343.   mov [justFound?], 0
  1344.   jmp JL1                       ;Jump to address
  1345.  
  1346. GetInt:
  1347. ;Get a decimal integer from keyboard to AX.  Carry set on improper input.
  1348. ;Abort if null input.
  1349.   push bx
  1350.   push cx
  1351.   push dx
  1352.   push si
  1353.   mov dx, OFFSET buffer
  1354.   call GetString                ;Input a string
  1355.   mov cx, ax                    ;Construct integer a digit at a time
  1356.   mov si, OFFSET buffer
  1357.   sub ax, ax
  1358.   mov bh, 10
  1359. @@L1:
  1360.   mov bl, [si]                  ;Get next char
  1361.   inc si
  1362.   sub bl, '0'
  1363.   jc @@Lx                       ;Exit with carry set if not a digit
  1364.   cmp bl, '9'
  1365.   cmc
  1366.   jc @@Lx
  1367.   mul bh                        ;AX := AX + (new digit - '0')
  1368.   add al, bl
  1369.   adc ah, 0
  1370.   jc @@Lx                       ;Check for overflow
  1371.   loop @@L1                     ;Next char
  1372. @@Lx:
  1373.   pop si                        ;Return with int in AX, carry set if error
  1374.   pop dx
  1375.   pop cx
  1376.   pop bx
  1377.   ret
  1378. @@La:
  1379.   mov si, OFFSET cancelledMsg
  1380.   jmp Abort
  1381.  
  1382. SetLabel:
  1383. ;Set label 0-9 at current line
  1384.   call GetLabel
  1385.   mov [si], bx
  1386.   ret
  1387.  
  1388. GotoLabel:
  1389. ;Goto label 0-9 previously set by SetLabel
  1390.   call GetLabel
  1391.   cmp [WORD si], 0
  1392.   je JLx
  1393.   mov bx, [si]                  ;Retrieve address
  1394.   mov [justFound?], 0
  1395. JL1:
  1396.   mov ax, bx
  1397.   sub ax, 8                     ;Make cursor line fifth from top
  1398.   cmp ax, OFFSET linePtrs
  1399.   jge @@L1
  1400.   mov ax, OFFSET linePtrs
  1401. @@L1:
  1402.   mov [top], ax
  1403. JLx:
  1404.   jmp NewLine0
  1405.  
  1406. GetLabel:
  1407.   mov si, OFFSET setLabelMsg
  1408.   call Prompt
  1409.   mov ah, 8                     ;Get char from keyboard
  1410.   int 21h
  1411.   mov dl, al                    ;Save copy to echo
  1412.   sub al, '0'                   ;Don't accept input if not a digit
  1413.   jl GetLabel
  1414.   cmp al, 9
  1415.   jg GetLabel
  1416.   mov ah, 2
  1417.   mov cl, al
  1418.   int 21h
  1419.   mov si, OFFSET LabelTable     ;Form index into LabelTable
  1420.   shl cl, 1
  1421.   sub ch, ch
  1422.   add si, cx                    ;Return address of label storage in SI
  1423.   ret
  1424.  
  1425. SetTabs:
  1426. ;Set tab width
  1427.   mov si, OFFSET setTabsMsg
  1428.   call Prompt
  1429.   call GetInt
  1430.   mov [tabSize], al
  1431.   ret
  1432.  
  1433. AutoIndent:
  1434. ;Toggle autoindent mode
  1435.   not [autoIndent?]
  1436.   ret
  1437.  
  1438. Kill:
  1439. ;Clear changed? flag so file changes will be discarded on exit
  1440.   mov [changed?], 0
  1441.   ret
  1442.  
  1443. Save:
  1444. ;Write lines to file, renaming old version with .BAK extension
  1445.   push dx
  1446.   push di
  1447.   push es
  1448.   push bx
  1449.   mov al, [changed?]            ;If no changes, done.
  1450.   or al, al
  1451.   jnz @@L0
  1452.   jmp XSave
  1453. @@L0:
  1454.   mov al, [newFile?]            ;If a new file, create it first
  1455.   or al, [isBAKed?]             ;If already BAKed up, no .BAK needed
  1456.   jnz DoSave
  1457.   mov ah, 3Eh                   ;Else close file
  1458.   mov bx, [fHandle]
  1459.   int 21h
  1460.   mov ax, ds
  1461.   mov es, ax
  1462.   mov si, OFFSET fName          ;Make new ASCIIZ string with .BAK extension
  1463.   mov di, OFFSET fNameBAK
  1464. @@L1:
  1465.   lodsb
  1466.   cmp al, '.'
  1467.   je @@L2
  1468.   or al, al
  1469.   je @@L2
  1470.   stosb
  1471.   jmp SHORT @@L1
  1472. @@L2:
  1473.   mov cx, 4
  1474.   mov si, OFFSET BAK
  1475.   rep movsb
  1476.   mov ah, 41h                   ;Delete old back-up copy
  1477.   mov dx, OFFSET fNameBAK
  1478.   int 21h
  1479.   mov dx, OFFSET fName          ;Rename current file to file.BAK
  1480.   mov di, OFFSET fNameBAK
  1481.   mov ah, 56h
  1482.   int 21h
  1483. DoSave:
  1484.   mov ah, 3Ch                   ;CREATe new file with old name
  1485.   sub cx, cx
  1486.   mov dx, OFFSET fName
  1487.   int 21h
  1488.   jc CantOpen
  1489.   mov [fHandle], ax
  1490.   mov [isBAKed?], -1            ;Set flag so we only make .BAK file once
  1491.   mov bx, OFFSET linePtrs       ;Write file
  1492.   call WriteFile
  1493. XSave:
  1494.   pop bx
  1495.   pop es
  1496.   pop di
  1497.   pop dx
  1498.   ret
  1499.  
  1500. WriteFile:
  1501. ;Write lines out to file starting at BX and ending at [lastLine]
  1502.   push es
  1503.   push di
  1504.   mov di, OFFSET buffer
  1505. @@L1:
  1506.   mov si, di                    ;Preserve file buffer pointer
  1507.   mov es, [bx]                  ;Strip trailing blanks
  1508.   mov cx, LINELENGTH
  1509.   mov di, LINELENGTH - 1
  1510.   mov al, ' '
  1511.   std
  1512.   repe scasb
  1513.   cld
  1514.   je @@L1a
  1515.   inc cx
  1516. @@L1a:
  1517.   mov ax, ds                    ;Copy line to file buffer
  1518.   mov dx, es
  1519.   mov es, ax
  1520.   mov ds, dx
  1521.   mov di, si
  1522.   sub si, si
  1523.   rep movsb
  1524.   mov ax, CRLF                  ;Stick a CRLF on the end
  1525.   stosw
  1526.   mov ax, ds
  1527.   mov dx, es
  1528.   mov es, ax
  1529.   mov ds, dx
  1530.   cmp di, OFFSET Buffer + BUFFERLENGTH - 80  ;If buffer is almost full,
  1531.   jl @@L2
  1532.   call WriteBuffer              ; write it
  1533. @@L2:
  1534.   inc bx                        ;Next line, loop until all lines are written
  1535.   inc bx
  1536.   cmp bx, [lastLine]
  1537.   jle @@L1
  1538.   call WriteBuffer              ;Write final partial buffer to file and exit
  1539.   pop di
  1540.   pop es
  1541.   ret
  1542.  
  1543. FileError:
  1544.   mov si, OFFSET fileErrorMsg
  1545.   jmp Abort
  1546. CantOpen:
  1547.   mov si, OFFSET cantOpenMsg
  1548.   jmp Abort
  1549. NoRoom:
  1550.   mov si, OFFSET noRoomMsg
  1551.   jmp Abort
  1552. ReadError:
  1553.   mov si, OFFSET rdErrorMsg
  1554.   jmp Abort
  1555.  
  1556. WriteBuffer:
  1557. ;Write text in buffer to disk
  1558.   push bx
  1559.   mov ah, 40h
  1560.   mov bx, [fHandle]
  1561.   mov cx, di
  1562.   mov dx, OFFSET Buffer
  1563.   sub cx, dx
  1564.   jz @@L1
  1565.   int 21h
  1566.   jc FileError
  1567.   mov di, OFFSET Buffer
  1568. @@L1:
  1569.   pop bx
  1570.   ret
  1571.  
  1572. Exit:
  1573.   call Save                     ;Save file if changed
  1574.   mov cx, 0C0Dh                 ;Restore standard cursor size
  1575.   mov ah, 1
  1576.   int 10h
  1577.   mov dx, 1800h                 ;Put cursor at bottom of screen
  1578.   sub bh, bh
  1579.   mov ah, 2
  1580.   int 10h
  1581.   mov ax, 4C00h                 ;Bye!
  1582.   int 21h
  1583.  
  1584. BeginBlock:
  1585. ;Start marking block for block operation
  1586.   mov [marking?], -1
  1587.   mov [mark], bx
  1588.   ret
  1589.  
  1590. Unmark:
  1591. ;Clear marking
  1592.   mov [marking?], 0
  1593.   ret
  1594.  
  1595. InsertBlock:
  1596. ;Insert from buffer or named file
  1597.   push bx
  1598.   call FileOrBuffer?            ;From file or buffer?
  1599.   je InsertBuffer
  1600.   mov dx, OFFSET fNameBlock     ;If file, open it
  1601.   mov ax, 3D00h
  1602.   int 21h
  1603.   jnc @@L1
  1604.   jmp CantOpen
  1605. @@L1:
  1606.   mov dx, ax                    ;Load file
  1607.   mov bx, OFFSET blockPtrs                                 
  1608.   mov di, bx
  1609.   call ReadFile
  1610.   mov cx, bx
  1611.   mov bx, dx                    ;Close file
  1612.   mov ah, 3Eh
  1613.   int 21h
  1614.   jmp SHORT DoInsert
  1615. InsertBuffer:                   ;Insert from buffer
  1616.   mov bp, [blockPtrsLast]       ;Abort if empty
  1617.   cmp bp, OFFSET blockPtrs
  1618.   jne @@L0
  1619.   pop bx
  1620.   jmp na
  1621. @@L0:
  1622.   test [needCopies?], -1        ;If not just moving lines, need to duplicate
  1623.   jz @@L2
  1624.   push bx
  1625.   push es
  1626.   push ds
  1627.   mov bx, bp
  1628.   mov dx, [heapPtr]
  1629. @@L1:
  1630.   dec bx                        ;Copy contents of buffered lines to new ones
  1631.   dec bx
  1632.   mov ds, [cs:bx]
  1633.   sub si, si
  1634.   mov es, dx
  1635.   sub di, di
  1636.   mov [cs:bx], es
  1637.   mov cx, LINELENGTH
  1638.   rep movsb
  1639.   add dx, 5
  1640.   cmp dx, 0A000h
  1641.   jb @@L1a
  1642.   jmp TooBig
  1643. @@L1a:
  1644.   cmp bx, OFFSET blockPtrs
  1645.   ja @@L1
  1646.   pop ds
  1647.   pop es
  1648.   pop bx
  1649.   mov [heapPtr], dx
  1650. @@L2:
  1651.   mov [needCopies?], -1
  1652.   mov cx, bp
  1653. DoInsert:
  1654.   pop bx
  1655.   sub cx, OFFSET blockPtrs      ;Get count of lines to move
  1656.   shr cx, 1
  1657.   call OpenRow                  ;Open that much room in array of seg pointers
  1658.   mov si, OFFSET blockPtrs      ;Copy new lines into opening
  1659.   mov di, bx
  1660.   mov ax, ds
  1661.   mov es, ax
  1662.   rep movsw
  1663.   mov [changed?], -1
  1664.   sub di, di
  1665.   jmp NewLine
  1666.  
  1667. FileOrBuffer?:
  1668. ;Prompt for file name if Shift is down, put it in fNameBlock.
  1669. ;If return with Z set, Shift was up, indicating buffer is to be used.
  1670.   call Shifted?                 ;If shift is down, prompt for file name
  1671.   jz Ret4
  1672.   mov dx, OFFSET fNameBlock
  1673.   call GetFileName
  1674.   jnz Ret4                      ;If null string returned, abort
  1675.   mov si, OFFSET cancelledMsg
  1676.   jmp Abort
  1677.  
  1678. Shifted?:
  1679.   mov ah, 2                     ;Get shift key status
  1680.   int 16h
  1681.   and al, 3
  1682. Ret4:
  1683.   ret
  1684.  
  1685. EmptyBuffer:
  1686. ;If Shifted, write block buffer to file, else discard
  1687.   mov bp, [blockPtrsLast]       ;Abort if buffer is empty
  1688.   cmp bp, OFFSET blockPtrs
  1689.   jne @@L0
  1690.   jmp Beep
  1691. @@L0:
  1692.   call Shifted?                 ;If shifted, write to file
  1693.   je @@L1
  1694.   push bx
  1695.   mov dx, OFFSET fNameBlock
  1696.   call GetFileName
  1697.   mov si, OFFSET blockPtrs
  1698.   mov bx, bp
  1699.   dec bx
  1700.   dec bx
  1701.   call WriteBlock
  1702.   pop bx
  1703. @@L1:
  1704.   mov ax, OFFSET blockPtrs      ;Else just reset buffer pointer
  1705.   mov [blockPtrsLast], ax
  1706. @@Lx:
  1707.   ret
  1708.  
  1709. WriteBlock:
  1710. ;Write block to file.  SI -> starting seg pointer, BX -> ending seg pointer.
  1711.   push [lastLine]               ;Copy block buffered lines to file:
  1712.   push [fHandle]
  1713.   mov ah, 3Ch                   ;CREATe file
  1714.   sub cx, cx
  1715.   mov dx, OFFSET fNameBlock
  1716.   int 21h
  1717.   jnc @@L1
  1718.   jmp CantOpen
  1719. @@L1:
  1720.   mov [fHandle], ax             ;Write it
  1721.   mov [lastLine], bx
  1722.   mov bx, si
  1723.   call WriteFile
  1724.   mov bx, [fHandle]             ;Close it
  1725.   mov ah, 3Eh
  1726.   int 21h
  1727.   pop [fHandle]
  1728.   pop [lastLine]
  1729.   ret
  1730.  
  1731. Copy:
  1732. ;Copy marked lines, to file if shift is down, otherwise to buffer.
  1733.   test [marking?], -1          ;Abort with a beep if not already marking,
  1734.   jnz @@L1
  1735.   mov si, OFFSET notMarkingMsg
  1736.   jmp Abort
  1737. @@L1:
  1738.   push bx
  1739.   push di
  1740.   push es
  1741.   mov si, [mark]
  1742.   cmp bx, si                    ;If mark comes after here, exchange
  1743.   jae @@L2
  1744.   xchg bx, si
  1745. @@L2:
  1746.   mov [mark], si                ; save in this order for possible delete
  1747.   call FileOrBuffer?            ;If Shift key was down when command entered,
  1748.   je @@L4
  1749.   call WriteBlock
  1750.   mov di, OFFSET blockPtrs
  1751.   jmp @@Lx
  1752. @@L4:
  1753.   mov cx, bx                    ;If no Shift, move marked lines to buffer
  1754.   sub cx, si
  1755.   shr cx, 1
  1756.   inc cx
  1757.   mov di, OFFSET blockPtrs
  1758.   mov ax, ds
  1759.   mov es, ax
  1760.   rep movsw
  1761. @@Lx:
  1762.   mov [blockPtrsLast], di       ;Save pointer to last line
  1763.   pop es
  1764.   pop di
  1765.   pop bx
  1766.   mov [marking?], 0
  1767.   ret
  1768.  
  1769. DeleteBlock:
  1770. ;Do a Copy, then delete copied lines
  1771.   call Copy                     ;Copy block to file or buffer
  1772.   mov si, bx                    ;Close up copied lines:
  1773.   inc si                        ;SI = cursor line + 2
  1774.   inc si
  1775.   mov di, [mark]                ;DI = start of marking
  1776.   mov cx, [lastLine]            ;CX = number of lines from here to end
  1777.   sub cx, bx
  1778.   push es
  1779.   mov ax, ds
  1780.   mov es, ax
  1781.   rep movsb
  1782.   pop es
  1783.   dec di
  1784.   dec di
  1785.   mov [lastLine], di            ;Store index of new last line
  1786.   sub di, di                    ;Point cursor to start of old marked line
  1787.   mov bx, [mark]
  1788.   mov [needCopies?], 0
  1789.   mov [changed?], -1
  1790.   jmp NewLine
  1791.  
  1792. Find:
  1793. ;Prompt for and find next <string>.  If Shifted, reuse last <string>.
  1794.   push bx
  1795.   push es
  1796.   push di
  1797.   call Right
  1798.   test [autoReplace?], -1       ;If doing replace all, bypass shift check
  1799.   js @@L0
  1800.   jnz @@L1
  1801.   call Shifted?                 ;If Shifted,
  1802.   jnz @@L1
  1803. @@L0:
  1804.   mov si, OFFSET findMsg        ;Get search string
  1805.   mov dx, OFFSET findString
  1806.   call GetCountedString
  1807. @@L1:
  1808.   mov si, OFFSET findString
  1809.   lodsw
  1810.   dec al
  1811.   mov dl, al
  1812.   mov al, ah
  1813.   mov cx, LINELENGTH
  1814.   sub cx, di
  1815. FindLoop:
  1816.   repne scasb                   ;Scan for first char of Find string
  1817.   jne FindNextLine
  1818.   push di                       ;Once found, compare rest of string
  1819.   mov dh, cl
  1820.   mov cl, dl
  1821.   mov si, OFFSET findString + 2
  1822.   repe cmpsb
  1823.   je Found
  1824.   pop di                        ;Match failed.  Scan again for 1st char.
  1825.   mov cl, dh
  1826.   jmp FindLoop                                             
  1827. FindNextLine:
  1828.   inc bx                        ;Search next line (until EOF)
  1829.   inc bx
  1830.   mov es, [bx]
  1831.   sub di, di
  1832.   mov cl, LINELENGTH
  1833.   cmp bx, [lastLine]
  1834.   jbe FindLoop
  1835.   mov si, OFFSET notFoundMsg    ;Not found
  1836.   test [autoReplace?], -1       ;If doing auto-replace, return
  1837.   jz @@L1
  1838.   mov [autoReplace?], 0
  1839.   pop di
  1840.   pop es
  1841.   pop bx
  1842.   ret
  1843. @@L1:
  1844.   call Print0                   ;Else restore cursor, abort with error message
  1845.   pop di
  1846.   pop es
  1847.   pop bx
  1848.   jmp na
  1849. Found:
  1850.   pop di
  1851.   dec di
  1852.   add sp, 6
  1853.   mov [justFound?], -1
  1854.   mov ax, bx                    ;Show found line 5 below top of screen
  1855.   jmp JL1
  1856.  
  1857. GetCountedString:
  1858. ;Print prompt string at [SI], read counted string into [DX].
  1859. ;Returns count (minus CR/LF) in AL.  DX is advanced one to start of string.
  1860.   push di
  1861.   push dx
  1862.   call Prompt                   ;Display prompt
  1863.   pop dx
  1864.   mov di, dx
  1865.   inc dx
  1866.   call GetString                ;Get input string
  1867.   mov [di], al                  ;Store count in front of string
  1868.   pop di
  1869.   ret
  1870.  
  1871. Replace:
  1872.   push di
  1873.   test [autoReplace?], -1       ;If using auto-replace command,
  1874.   jz @@L0
  1875.   jns @@L2                      ;Skip shift check
  1876.   mov [autoReplace?], 1
  1877.   jmp SHORT @@L1a               ;Get replace string if shifted and first pass
  1878. @@L0:
  1879.   test [justFound?], -1         ;Beep if not immediately preceded by a Find
  1880.   jnz @@L1
  1881. @@LE:
  1882.   pop di
  1883.   jmp na
  1884. @@L1:
  1885.   call Shifted?                 ;If not Shifted, prompt for replace string
  1886.   jnz @@L2
  1887. @@L1a:
  1888.   mov si, OFFSET replaceMsg
  1889.   mov dx, OFFSET replaceString
  1890.   call GetCountedString
  1891. @@L2:
  1892.   mov si, OFFSET replaceString
  1893.   lodsb
  1894.   sub ah, ah
  1895.   mov dx, si
  1896.   push ax
  1897.   push dx
  1898.   sub ch, ch                    ;Compare lengths of find and replace strings
  1899.   mov cl, al
  1900.   sub cl, [byte findString]     ;If replace string is longer,
  1901.   je @@L6
  1902.   jb @@L4
  1903.   xchg dx, di
  1904.   call EndLine                  ; make sure there will be enough room on line
  1905.   xchg dx, di
  1906.   add dl, cl
  1907.   cmp dl, LINELENGTH
  1908.   ja @@LE
  1909. @@L3:
  1910.   call Insert0                  ; then insert extra characters
  1911.   loop @@L3
  1912.   jmp SHORT @@L6
  1913. @@L4:
  1914.   neg cl                        ;If shorter, delete difference
  1915. @@L5:
  1916.   call Delete0
  1917.   loop @@L5
  1918. @@L6:
  1919.   pop si                        ;Now copy new string over old
  1920.   pop cx
  1921.   sub ch, ch
  1922.   pop di
  1923.   rep movsb
  1924.   mov [changed?], -1
  1925.   jmp NewLine
  1926.  
  1927. AutoReplace:
  1928. ;Find and replace all.  If shift not down, prompt for strings.
  1929.   push [top]
  1930.   push es
  1931.   push di
  1932.   push bx
  1933.   mov [autoReplace?], 1         ;Set flag: 1 = no shift, -1 = shift
  1934.   call Shifted?
  1935.   jne @@L1
  1936.   mov [autoReplace?], -1
  1937. @@L1:
  1938.   call Find                     ;Find/replace until flag reset by not found
  1939.   test [autoReplace?], -1
  1940.   jz @@Lx
  1941.   call Replace
  1942.   jmp @@L1
  1943. @@Lx:
  1944.   pop bx                        ;Restore original position
  1945.   pop di
  1946.   pop es
  1947.   pop [top]
  1948.   push si
  1949.   call ReDraw                   ;Refresh screen with any changes
  1950.   pop si
  1951.   push di
  1952.   call Print0                   ;Show notFoundMsg and exit
  1953.   pop di
  1954.   jmp NextNoRedraw
  1955.  
  1956. Help:
  1957.   push es
  1958.   push di
  1959.   mov es, [videoSegment]
  1960.   mov di, 160
  1961.   mov si, OFFSET helpMsg
  1962.   mov cx, 80 * 24
  1963.   mov ah, [attribNl]
  1964. @@L1:
  1965.   lodsb
  1966.   stosw
  1967.   loop @@L1
  1968.   mov si, OFFSET endHelpMsg
  1969.   call Print0
  1970.   sub ah, ah
  1971.   int 16h
  1972.   pop di
  1973.   pop es
  1974.   ret
  1975.  
  1976. END
  1977.  
  1978.